/* 
 * File:   tssproxy.h
 * Author: liushihai 
 * thread specific storage proxy template
 * Created on 2012年8月2日, 下午2:45
 */

#ifndef COMMON_THREAD_TSS_PROXY_H_
#define	COMMON_THREAD_TSS_PROXY_H_

#include <pthread.h>
#include "simplelock.h"
#include "autodone.h"



namespace thread {   

	// thread specific storage proxy template 
	template <class TYPE>
		class TSSProxy {
			public:
				// Constructor. 
				TSSProxy();

				// Destructor 
				~TSSProxy();

				// Use the C++ "smart pointer" operator to 
				// access the thread-specific TYPE object. 
				TYPE *operator->();

			private:
				// Key for the thread-specific error data. 
				pthread_key_t key_;

				// "First time in" flag. 
				int once_;

				// Avoid race conditions during initialization. 
				SimpleLock  lock_;

				// Cleanup hook that deletes dynamically 
				// allocated memory. 
				static void cleanup_hook(void *ptr);
		};

	template <class TYPE> TSSProxy<TYPE>::TSSProxy(): key_(0), once_(0){} 

	template <class TYPE> TSSProxy<TYPE>::~TSSProxy (){}

	template <class TYPE> TYPE * TSSProxy<TYPE>::operator->() 
	{
		TYPE *tss_data = 0;

		// Use the Double-Checked Locking pattern to 
		// avoid locking except during initialization. 
		// First check. 
		if (this->once_ == 0) 
		{
			// Ensure that we are serialized (constructor 
			// of Guard acquires the lock). 
			AutoDone<SimpleLock> guard( lock_, &SimpleLock::Lock, &SimpleLock::Unlock );

			// Double check 
			if (this->once_ == 0) 
			{
				pthread_key_create(&this->key_, &this->cleanup_hook);

				// *Must* come last so that other threads 
				// don’t use the key until it’s created. 
				this->once_ = 1;
			}
		}

		// Get the data from thread-specific storage. 
		// Note that no locks are required here... 
		tss_data =  (TYPE*)pthread_getspecific(this->key_); 

		// Check to see if this is the first time in 
		// for this thread. 
		if (tss_data == NULL) 
		{
			// Allocate memory off the heap and store 
			// it in a pointer in thread-specific 
			// storage (on the stack...). 
			tss_data = new TYPE;

			// Store the dynamically allocated pointer in 
			// thread-specific storage. 
			pthread_setspecific(this->key_, (void *) tss_data);
		}

		return tss_data;
	}

	template <class TYPE> void TSSProxy<TYPE>::cleanup_hook (void *ptr) 
	{ 
		// This cast is necessary to invoke 
		// the destructor (if it exists). 
		if(ptr != NULL)
		{
			delete (TYPE *) ptr; 
			ptr = NULL;
		}
	}

}



#endif	/* YY_COMMON_THREAD_TSS_PROXY_H_ */

